#!/usr/bin/env python3
#
# vim: set expandtab shiftwidth=4 tabstop=4:
#
# This file is part of libratbag.
#
# Copyright 2017 Red Hat, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice (including the next
# paragraph) shall be included in all copies or substantial portions of the
# Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.

import argparse
import io
import os
import resource
import subprocess
import sys
import toolbox
import unittest

run_ratbagctl_in_subprocess = False


class TestRatbagCtl(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        super(TestRatbagCtl, cls).setUpClass()
        cls.reset_test_device()
        cls.find_test_device()

    @classmethod
    def run_ratbagctl_subprocess(cls, params):
        ratbagctl = subprocess.Popen(" ".join([toolbox.RATBAGCTL_PATH, params]),
                                     stdout=subprocess.PIPE,
                                     stderr=subprocess.PIPE,
                                     shell=True,
                                     env={"RATBAGCTL_DEVEL": "org.freedesktop.ratbag_devel1_@ratbagd_sha@"})

        (stdout, stderr) = ratbagctl.communicate()
        return (ratbagctl.returncode,
                stdout.decode('utf-8').rstrip('\n'),
                stderr.decode('utf-8').rstrip('\n'))

    @classmethod
    def run_ratbagctl_fast(cls, params):
        global parser, ratbagd
        stdout = sys.stdout
        stderr = sys.stderr
        returncode = 0

        sys.stdout = io.StringIO()
        sys.stderr = io.StringIO()
        try:
            cmd = parser.parse(params.split())
            cmd.func(ratbagd, cmd)
        except SystemExit as e:
            returncode = e.code
        except AttributeError as e:
            returncode = 2
        except argparse.ArgumentTypeError as e:
            returncode = 2
        output = sys.stdout.getvalue()
        error = sys.stderr.getvalue()
        sys.stdout = stdout
        sys.stderr = stderr
        toolbox.sync_dbus()
        return returncode, output.rstrip('\n'), error.rstrip('\n')

    @classmethod
    def run_ratbagctl(cls, params):
        params = params.replace('test_device', cls.test_device)
        if run_ratbagctl_in_subprocess:
            return cls.run_ratbagctl_subprocess(params)
        return cls.run_ratbagctl_fast(params)

    @classmethod
    def reset_test_device(cls):
        global ratbagd
        ratbagd._dbus_call("ResetTestDevice", "")
        toolbox.sync_dbus()

    @classmethod
    def find_test_device(cls):
        rc, stdout, stderr = cls.run_ratbagctl_subprocess('list')
        testdevice = None
        for line in stdout.split('\n'):
            if 'Test device' in line:
                testdevice = line.split(':')[0]
                break

        assert testdevice is not None
        cls.test_device = testdevice

    def launch_good_test(self, params):
        returncode, stdout, stderr = self.run_ratbagctl(params)
        self.assertEqual(returncode, 0, msg=stderr + stdout)
        self.maxDiff = None
        self.assertEqual(stderr, '')
        return stdout

    def launch_fail_test(self, params):
        returncode, stdout, stderr = self.run_ratbagctl(params)
        self.assertNotEqual(returncode, 0, msg=stderr + stdout)
        return stdout

    def launch_fail_with_exception_test(self, params, exception):
        with self.assertRaises(exception):
            returncode, stdout, stderr = self.run_ratbagctl(params)
        return ''

    @classmethod
    def setProfile(cls, profile):
        cls.run_ratbagctl("test_device profile active set {}".format(profile))


class TestRatbagCtlList(TestRatbagCtl):
    def test_list(self):
        r = self.launch_good_test("list")
        self.assertIn(self.test_device, r)
        self.launch_fail_test("test_device list")
        self.launch_fail_test("list test_device")


class TestRatbagCtlInfo(TestRatbagCtl):
    def test_info(self):
        self.launch_good_test("test_device info")
        self.launch_fail_test("test_device info X")
        self.launch_fail_test("info X")


class TestRatbagCtlName(TestRatbagCtl):
    def test_get(self):
        r = self.launch_good_test("test_device name")
        self.assertEqual(r, 'Test device')
        self.launch_fail_test("test_device name X")
        self.launch_fail_test("name X")


class TestRatbagCtlProfile(TestRatbagCtl):
    @classmethod
    def setUpClass(cls):
        super(TestRatbagCtlProfile, cls).setUpClass()
        cls.setProfile(0)
        cls.setProfile(1)
        cls.setProfile(0)

    def test_profile_name_get(self):
        command = "profile 1 name get"
        r = self.launch_good_test("test_device " + command)
        self.assertEqual(r, 'test profile 2')
        self.launch_fail_test(command)
        self.launch_fail_test("test_device " + command + " X")
        self.launch_fail_test("test_device profile name get")

    def test_profile_name_set(self):
        r = self.launch_good_test("test_device profile 1 name get")
        self.assertEqual(r, 'test profile 2')
        self.launch_good_test("test_device profile 1 name set banana")
        r = self.launch_good_test("test_device profile 1 name get")
        self.assertEqual(r, 'banana')
        # profile 0 doesn't have the capability RATBAG_PROFILE_CAP_WRITABLE_NAME
        self.launch_fail_with_exception_test("test_device profile 0 name set kiwi", toolbox.RatbagErrorCapability)
        # better be safe than sorry, checking that the previous actually failed :)
        r = self.launch_good_test("test_device profile 0 name get")
        self.assertNotEqual(r, 'kiwi')
        self.launch_fail_test("profile 1 name set blah")
        self.launch_fail_test("test_device profile 1 name set blah X")
        self.launch_fail_test("test_device profile name set blah")

    def test_profile_active_get(self):
        command = "profile active get"
        r = self.launch_good_test("test_device " + command)
        self.assertEqual(int(r), 0)
        self.launch_fail_test(command)
        self.launch_fail_test("test_device " + command + " X")

    def test_profile_active_set(self):
        command = "profile active set"
        r = self.launch_good_test("test_device " + command + " 1")
        self.assertEqual(r, '')
        r = self.launch_good_test("test_device profile active get")
        self.assertEqual(int(r), 1)
        self.launch_fail_test(command)
        self.launch_fail_test("test_device " + command + " X")
        self.launch_fail_test("test_device " + command + " 1 X")

    def test_profile_n(self):
        r0 = self.launch_good_test("test_device profile 0 get")
        r1 = self.launch_good_test("test_device profile 1 get")
        self.assertNotEqual(r0, r1)
        self.launch_fail_test("profile 0 get")
        self.launch_fail_test("test_device profile 0")
        self.launch_fail_test("test_device profile 0 get X")
        self.launch_fail_test("test_device profile 10 get")

    def test_profile_enable(self):
        r = self.launch_good_test("test_device profile 0 get")
        self.assertNotIn('disabled', r)
        r = self.launch_good_test("test_device profile 3 get")
        self.assertIn('disabled', r)
        self.launch_good_test("test_device profile 3 enable")
        r = self.launch_good_test("test_device profile 3 get")
        self.assertNotIn('Profile 3: (disabled)', r)
        self.launch_fail_test("profile enable")
        self.launch_fail_test("test_device profile X enable")
        self.launch_fail_test("test_device profile 1 enable X")
        self.launch_fail_test("test_device profile 10 enable")

    def test_profile_disable(self):
        r = self.launch_good_test("test_device profile 2 get")
        self.assertNotIn('disabled', r)
        self.launch_good_test("test_device profile 2 disable")
        r = self.launch_good_test("test_device profile 2 get")
        self.assertEqual('Profile 2: (disabled)', r)
        self.launch_fail_test("profile disable")
        self.launch_fail_test("test_device profile X disable")
        self.launch_fail_test("test_device profile 1 disable X")
        self.launch_fail_test("test_device profile 10 disable")


class TestRatbagCtlResolution(TestRatbagCtl):
    @classmethod
    def setUpClass(cls):
        super(TestRatbagCtlResolution, cls).setUpClass()
        cls.setProfile(1)

    def test_resolution_active_get(self):
        command = "resolution active get"
        r = self.launch_good_test("test_device " + command)
        self.assertEqual(int(r), 2)
        self.launch_fail_test("test_device " + command + " X")
        self.launch_fail_test("test_device " + command + " 10")

    def test_resolution_active_set(self):
        command = "resolution active set"
        r = self.launch_good_test("test_device resolution active get")
        self.assertEqual(int(r), 2)
        r = self.launch_good_test("test_device " + command + " 0")
        self.assertEqual(r, '')
        r = self.launch_good_test("test_device resolution active get")
        self.assertEqual(int(r), 0)
        self.launch_fail_test(command)
        self.launch_fail_test("test_device " + command + " X")
        self.launch_fail_test("test_device " + command + " 1 X")

    def test_resolution_n_get(self):
        r = self.launch_good_test("test_device resolution 1 get")
        self.assertEqual(r, "1: 1200x1300dpi (default)")
        self.launch_fail_test("test_device resolution 10 get")
        self.launch_fail_test("test_device resolution 1 get X")
        self.launch_fail_test("resolution 1 get X")

    def test_profile_resolution(self):
        r = self.launch_good_test("test_device profile 2 resolution 2 get")
        self.assertEqual(r, "2: 2300x2400dpi")

    def test_resolution_default_get(self):
        command = "resolution default get"
        r = self.launch_good_test("test_device " + command)
        self.assertEqual(int(r), 1)
        self.launch_fail_test("test_device " + command + " X")
        self.launch_fail_test("test_device " + command + " 10")

    def test_resolution_default_set(self):
        command = "resolution default set"
        self.launch_good_test("test_device " + command + " 2")
        r = self.launch_good_test("test_device resolution default get")
        self.assertEqual(int(r), 2)
        self.launch_fail_test(command)
        self.launch_fail_test("test_device " + command + " X")
        self.launch_fail_test("test_device " + command + " 1 X")
        # Reset for other tests
        self.launch_good_test("test_device " + command + " 1")


class TestRatbagCtlDPI(TestRatbagCtl):
    @classmethod
    def setUpClass(cls):
        super(TestRatbagCtlDPI, cls).setUpClass()
        cls.setProfile(0)

    def test_dpi_get(self):
        command = "dpi get"
        r = self.launch_good_test("test_device " + command)
        self.assertEqual(int(r[:-3]), 200)  # drop 'dpi' suffix
        self.setProfile(1)
        r = self.launch_good_test("test_device " + command)
        self.assertEqual(r, "1300x1400dpi")
        self.launch_fail_test(command)
        self.launch_fail_test("test_device " + command + " 1")
        self.launch_fail_test("test_device " + command + " X")

    def test_dpi_get_all(self):
        dpi_list = "50 100 150 200 250 300 350 400 450 500 550 600 650 700 750 800 850 900 950 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900 2000 2100 2200 2300 2400 2500 2600 2800 3000 3200 3400 3600 3800 4000 4200 4400 4600 4800 5000"
        command = "dpi get-all"
        r = self.launch_good_test("test_device " + command)
        self.assertEqual(r, dpi_list)
        self.setProfile(1)
        r = self.launch_good_test("test_device " + command)
        self.assertEqual(r, dpi_list)

    def test_dpi_set(self):
        command = "dpi set"
        self.setProfile(0)
        r = self.launch_good_test("test_device dpi get ")
        res = int(r[:-3]) + 50  # drop 'dpi' suffix
        r = self.launch_good_test("test_device " + command + " {}".format(res))
        self.assertEqual(r, '')
        r = self.launch_good_test("test_device dpi get")
        self.assertEqual(int(r[:-3]), res)  # drop 'dpi' suffix
        self.launch_fail_test(command)
        self.launch_fail_test("test_device " + command + " X")
        self.launch_fail_test("test_device " + command + " 100 X")

    def test_dpi_set_xy(self):
        command = "dpi set"
        self.setProfile(2)
        r = self.launch_good_test("test_device dpi get")
        self.assertEqual(r, "2100x2200dpi")
        self.launch_good_test("test_device " + command + " 1350x1450")
        r = self.launch_good_test("test_device dpi get")
        self.assertEqual(r, "1350x1450dpi")
        self.launch_good_test("test_device " + command + " 2350x2450dpi")
        r = self.launch_good_test("test_device dpi get")
        self.assertEqual(r, "2350x2450dpi")
        self.launch_good_test("test_device " + command + " 2350")
        r = self.launch_good_test("test_device dpi get")
        self.assertEqual(r, "2350x2350dpi")
        self.launch_fail_test("test_device " + command + " 2350dpi")
        self.launch_fail_test(command)
        self.launch_fail_test("test_device " + command + " X")
        self.launch_fail_test("test_device " + command + " 100 X")

    def test_prefix_dpi(self):
        self.setProfile(0)
        r = self.launch_good_test("test_device profile 1 dpi get")
        self.assertEqual(r, "1300x1400dpi")
        r = self.launch_good_test("test_device profile 1 resolution 1 dpi get")
        self.assertEqual(r, "1200x1300dpi")
        r = self.launch_good_test("test_device resolution 2 dpi get")
        self.assertEqual(int(r[:-3]), 300)  # drop 'dpi' suffix
        self.launch_fail_test("test_device profile 1 profile 1 dpi get")
        self.launch_fail_test("test_device profile 1 resolution 1 resolution 2 dpi get")


class TestRatbagCtlReportRate(TestRatbagCtl):
    @classmethod
    def setUpClass(cls):
        super(TestRatbagCtlReportRate, cls).setUpClass()
        cls.setProfile(0)

    def test_rate_get(self):
        command = "rate get"
        r = self.launch_good_test("test_device " + command)
        self.assertEqual(int(r), 1000)
        self.launch_fail_test(command)
        self.launch_fail_test("test_device " + command + " 1")
        self.launch_fail_test("test_device " + command + " X")

    def test_rate_get_all(self):
        rate_list = "500 1000"
        command = "rate get-all"
        r = self.launch_good_test("test_device " + command)
        self.assertEqual(r, rate_list)
        self.setProfile(1)
        r = self.launch_good_test("test_device " + command)
        self.assertEqual(r, rate_list)

    def test_rate_set(self):
        command = "rate set"
        r = self.launch_good_test("test_device rate get")
        res = int(r) + 500
        r = self.launch_good_test("test_device " + command + " {}".format(res))
        self.assertEqual(r, '')
        r = self.launch_good_test("test_device rate get")
        self.assertEqual(int(r), res)
        self.launch_fail_test(command)
        self.launch_fail_test("test_device " + command + " X")
        self.launch_fail_test("test_device " + command + " 100 X")

    def test_prefix_rate(self):
        r = self.launch_good_test("test_device rate get")
        self.assertEqual(int(r), 1000)
        r = self.launch_good_test("test_device profile 1 rate get")
        self.assertEqual(int(r), 2000)
        r = self.launch_good_test("test_device profile 2 rate get")
        self.assertEqual(int(r), 3000)
        self.launch_fail_test("test_device profile 1 profile 1 rate get")
        self.launch_fail_test("test_device profile 1 resolution 0 rate get")
        self.launch_fail_test("test_device profile 1 resolution 1 resolution 2 rate get")


class TestRatbagCtlButton(TestRatbagCtl):
    @classmethod
    def setUpClass(cls):
        super(TestRatbagCtlButton, cls).setUpClass()
        cls.setProfile(0)

    def test_button_count(self):
        command = "button count"
        r = self.launch_good_test("test_device " + command)
        self.assertEqual(int(r), 4)
        self.launch_fail_test(command)
        self.launch_fail_test("test_device " + command + " 1")
        self.launch_fail_test("test_device " + command + " X")

    def test_button_n_get(self):
        self.setProfile(0)
        r = self.launch_good_test("test_device button 0 get")
        self.assertEqual(r, "Button: 0 is mapped to 'button 0'")
        r = self.launch_good_test("test_device button 1 get")
        self.assertEqual(r, "Button: 1 is mapped to UNKNOWN")
        r = self.launch_good_test("test_device button 2 get")
        self.assertEqual(r, "Button: 2 is mapped to 'profile-cycle-up'")
        r = self.launch_good_test("test_device button 3 get")
        self.assertEqual(r, "Button: 3 is mapped to macro '↕B 300ms'")
        self.launch_fail_test("test_device button 1 get 10")
        self.launch_fail_test("test_device button 10 get")
        self.launch_fail_test("test_device button 1 get X")

    def test_button_n_action_get(self):
        self.setProfile(0)
        r = self.launch_good_test("test_device button 0 action get")
        self.assertEqual(r, "Button: 0 is mapped to 'button 0'")
        self.launch_fail_test("test_device button 1 action get 10")
        self.launch_fail_test("test_device button 10 action get")
        self.launch_fail_test("test_device button 1 action get X")

    def test_button_n_action_set_button(self):
        self.setProfile(2)
        command = "button 2 action set button"
        r = self.launch_good_test("test_device button 2 action get")
        self.assertEqual(r, "Button: 2 is mapped to 'button 2'")
        r = self.launch_good_test("test_device " + command + " 1")
        self.assertEqual(r, '')
        r = self.launch_good_test("test_device button 2 action get")
        self.assertEqual(r, "Button: 2 is mapped to 'button 1'")
        self.launch_fail_test(command)
        self.launch_fail_test("test_device " + command + " X")
        self.launch_fail_test("test_device " + command + " 100 X")

    def test_button_n_action_set_special(self):
        self.setProfile(2)
        command = "button 0 action set special "
        r = self.launch_good_test("test_device button 0 action get")
        self.assertEqual(r, "Button: 0 is mapped to 'profile-cycle-up'")
        r = self.launch_good_test("test_device " + command + " second-mode")
        self.assertEqual(r, '')
        r = self.launch_good_test("test_device button 0 action get")
        self.assertEqual(r, "Button: 0 is mapped to 'second-mode'")
        self.launch_fail_test(command)
        self.launch_fail_test("test_device " + command + " X")
        self.launch_fail_test("test_device " + command + " 100 X")

    def test_button_n_action_set_macro(self):
        command = "button 2 action set macro "
        self.launch_good_test("test_device " + command + '+KEY_B KEY_A -KEY_B KEY_C')
        r = self.launch_good_test("test_device button 2 get")
        self.assertEqual(r, "Button: 2 is mapped to macro '↓B ↕A ↑B ↕C'")
        self.launch_good_test("test_device " + command + 't100 t200')
        r = self.launch_good_test("test_device button 2 get")
        self.assertEqual(r, "Button: 2 is mapped to macro '100ms 200ms'")
        self.launch_good_test("test_device " + command + 'KeY_z')
        r = self.launch_good_test("test_device button 2 get")
        self.assertEqual(r, "Button: 2 is mapped to macro '↕Z'")
        self.launch_good_test("test_device " + command)
        r = self.launch_good_test("test_device button 2 get")
        self.assertEqual(r, "Button: 2 is mapped to macro 'None'")
        self.launch_fail_test(command)
        self.launch_fail_test("test_device " + command + " KEY_A X")

    def test_prefix_button(self):
        r = self.launch_good_test("test_device profile 2 button 3 get")
        self.assertEqual(r, "Button: 3 is mapped to 'button 3'")
        self.launch_fail_test("test_device profile 2 profile 2 button 3 get")
        self.launch_fail_test("test_device profile 2 resolution 2 button 3 get")


class TestRatbagCtlLED(TestRatbagCtl):
    @classmethod
    def setUpClass(cls):
        super(TestRatbagCtlLED, cls).setUpClass()
        cls.setProfile(0)

    def test_led_n_get(self):
        self.setProfile(0)
        r = self.launch_good_test("test_device led 0 get")
        self.assertEqual(r, "LED: 0, depth: rgb, mode: off")
        r = self.launch_good_test("test_device led 1 get")
        self.assertEqual(r, "LED: 1, depth: rgb, mode: on, color: ff0000")
        r = self.launch_good_test("test_device led 2 get")
        self.assertEqual(r, "LED: 2, depth: rgb, mode: cycle, duration: 333, brightness: 40")
        self.launch_fail_test("test_device led 0 get 10")
        self.launch_fail_test("test_device led 10 get")
        self.launch_fail_test("test_device led 0 get X")

    def test_led_n_set_mode(self):
        self.setProfile(0)
        r = self.launch_good_test("test_device led 0 get")
        self.assertEqual(r, "LED: 0, depth: rgb, mode: off")
        r = self.launch_good_test("test_device led 0 set mode cycle")
        self.assertEqual(r, '')
        r = self.launch_good_test("test_device led 0 get")
        self.assertEqual(r, "LED: 0, depth: rgb, mode: cycle, duration: 1000, brightness: 20")
        self.launch_fail_test("test_device led 0 set mode bicycle")
        self.launch_fail_test("test_device led 0 set mode")
        self.launch_fail_test("test_device led 0 set mode cycle X")

    def test_led_n_set_color(self):
        self.setProfile(0)
        r = self.launch_good_test("test_device led 1 get")
        self.assertEqual(r, "LED: 1, depth: rgb, mode: on, color: ff0000")
        r = self.launch_good_test("test_device led 1 set color 00ff00")
        self.assertEqual(r, '')
        r = self.launch_good_test("test_device led 1 get")
        self.assertEqual(r, "LED: 1, depth: rgb, mode: on, color: 00ff00")
        r = self.launch_good_test("test_device led 1 set color 0x0000ff")
        self.assertEqual(r, '')
        r = self.launch_good_test("test_device led 1 get")
        self.assertEqual(r, "LED: 1, depth: rgb, mode: on, color: 0000ff")
        self.launch_fail_test("test_device led 1 set color g0ff00")
        self.launch_fail_test("test_device led 1 set color")
        self.launch_fail_test("test_device led 1 set color 00ff00 X")

    def test_led_n_set_duration(self):
        self.setProfile(0)
        r = self.launch_good_test("test_device led 2 get")
        self.assertEqual(r, "LED: 2, depth: rgb, mode: cycle, duration: 333, brightness: 40")
        r = self.launch_good_test("test_device led 2 set duration 10")
        self.assertEqual(r, '')
        r = self.launch_good_test("test_device led 2 get")
        self.assertEqual(r, "LED: 2, depth: rgb, mode: cycle, duration: 10, brightness: 40")
        self.launch_fail_test("test_device led 2 set duration this_is_not_an_int")
        self.launch_fail_test("test_device led 2 set duration")
        self.launch_fail_test("test_device led 2 set duration 100 X")

    def test_led_n_set_brightness(self):
        self.setProfile(2)
        r = self.launch_good_test("test_device led 1 get")
        self.assertEqual(r, "LED: 1, depth: rgb, mode: cycle, duration: 333, brightness: 40")
        r = self.launch_good_test("test_device led 1 set brightness 100")
        self.assertEqual(r, '')
        r = self.launch_good_test("test_device led 1 get")
        self.assertEqual(r, "LED: 1, depth: rgb, mode: cycle, duration: 333, brightness: 100")
        self.launch_fail_test("test_device led 1 set brightness this_is_not_an_int")
        self.launch_fail_test("test_device led 1 set brightness")
        self.launch_fail_test("test_device led 1 set brightness 100 X")
        self.launch_fail_test("test_device led 1 set brightness this_is_not_an_int")
        self.launch_fail_test("test_device led 1 set brightness 256")
        self.launch_fail_test("test_device led 1 set brightness -10")

    def test_led_n_set_combination(self):
        self.setProfile(2)
        r = self.launch_good_test("test_device led 0 get")
        self.assertEqual(r, "LED: 0, depth: rgb, mode: on, color: ff0000")
        r = self.launch_good_test("test_device led 0 set mode cycle brightness 100")
        r = self.launch_good_test("test_device led 0 get")
        self.assertEqual(r, "LED: 0, depth: rgb, mode: cycle, duration: 1000, brightness: 100")
        r = self.launch_good_test("test_device led 0 set mode cycle duration 100 brightness 255")
        r = self.launch_good_test("test_device led 0 get")
        self.assertEqual(r, "LED: 0, depth: rgb, mode: cycle, duration: 100, brightness: 255")
        r = self.launch_good_test("test_device led 0 set mode on color 00ff00")
        r = self.launch_good_test("test_device led 0 get")
        self.assertEqual(r, "LED: 0, depth: rgb, mode: on, color: 00ff00")
        r = self.launch_good_test("test_device led 0 set duration 100 brightness 200 mode cycle")
        r = self.launch_good_test("test_device led 0 get")
        self.assertEqual(r, "LED: 0, depth: rgb, mode: cycle, duration: 100, brightness: 200")
        r = self.launch_good_test("test_device led 0 set duration 100 brightness 200 mode cycle brightness 100")
        r = self.launch_good_test("test_device led 0 get")
        self.assertEqual(r, "LED: 0, depth: rgb, mode: cycle, duration: 100, brightness: 100")
        self.launch_fail_test("test_device led 0 set brightness 10 mode bicycle")

    def test_prefix_led(self):
        self.setProfile(2)
        r = self.launch_good_test("test_device profile 1 led 1 get")
        self.assertEqual(r, "LED: 1, depth: rgb, mode: off")
        self.launch_fail_test("test_device profile 1 profile 2 led 1 get")
        self.launch_fail_test("test_device profile 1 resolution 2 led 1 get")

    def test_led_capabilities(self):
        r = self.launch_good_test("test_device led 0 capabilities")
        self.assertEqual(r, "Modes: breathing, cycle, off, on")
        r = self.launch_good_test("test_device led 1 capabilities")
        self.assertEqual(r, "Modes: breathing, cycle, off, on")


def setUpModule():
    global ratbagd_process, ratbagd, parser

    ratbagd_process = toolbox.start_ratbagd()

    assert ratbagd_process is not None

    ratbagd = toolbox.open_ratbagd(ratbagd_process)
    assert ratbagd is not None

    parser = toolbox.get_parser()


def tearDownModule():
    global ratbagd_process
    toolbox.terminate_ratbagd(ratbagd_process)


def parse(input_string):
    global run_ratbagctl_in_subprocess
    parser_test = argparse.ArgumentParser(description="Testsuite for ratbagd/ratbagctl", add_help=False)
    parser_test.add_argument("--subprocess", action='store_true', default=False)
    ns, rest = parser_test.parse_known_args(input_string)
    run_ratbagctl_in_subprocess = ns.subprocess
    return rest


def main(argv):
    if not os.geteuid() == 0:
        print('Script must be run as root', file=sys.stderr)
        sys.exit(77)

    os.environ['RATBAG_TEST'] = '1'
    resource.setrlimit(resource.RLIMIT_CORE, (0, 0))

    args = parse(argv)

    unittest.main(argv=[sys.argv[0], *args])


if __name__ == "__main__":
    main(sys.argv[1:])
